﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;

namespace Project1
{
    public class RatSpawn
    {

        #region CRat
        public class CRat
        {
            public Model model;
            public Vector3 position;
            public Vector3 velocity;
            public float size;
            public int leftFrontLeg;
            public int rightFrontLeg;
            public int leftBackLeg;
            public int rightBackLeg;
            public float legAngel = 0;
            public int health = 1;
            public int tail1;
            public bool reachedEnd = false;
            public bool superRat = false;
        }

        #endregion

        private TurretGame game;
        private LinkedList<CRat> rats = new LinkedList<CRat>();
        private Model ratModel;
        private Random random = new Random();
        private int score = 0;
        private int numSpawn;
        private bool spawnSuperRats;

        public LinkedList<CRat> RatsList { get { return rats; } }

        public RatSpawn(TurretGame game, int numRats, bool spawnSuper)
        {
            this.game = game;
            numSpawn = numRats;
            spawnSuperRats = spawnSuper;
        }

        public int Score { get { return score; } }
        public int ratsLeft { get { return rats.Count; } }

        public void LoadContent(ContentManager content)
        {
            ratModel = content.Load<Model>("Rat-rigid");
            if (!spawnSuperRats)
            {
                while (rats.Count < numSpawn)
                {
                    CRat rat = new CRat();
                    rat.model = ratModel;
                    rat.position = RandomVector(-3000, 3000);
                    rat.position.Y = 0;
                    rat.velocity = rat.position;
                    rat.velocity.Normalize();
                    int velFactor = random.Next(3);
                    if (velFactor != 0)
                        rat.velocity *= velFactor;
                    rat.size = 10;

                    if (rat.position.Length() < 2000)
                        continue;

                    rat.leftFrontLeg = rat.model.Bones.IndexOf(rat.model.Bones["LeftFrontLeg"]);
                    rat.rightFrontLeg = rat.model.Bones.IndexOf(rat.model.Bones["RightFrontLeg"]);
                    rat.leftBackLeg = rat.model.Bones.IndexOf(rat.model.Bones["LeftBackLeg"]);
                    rat.rightBackLeg = rat.model.Bones.IndexOf(rat.model.Bones["RightBackLeg"]);

                    rat.tail1 = rat.model.Bones.IndexOf(rat.model.Bones["Tail1"]);

                    rats.AddLast(rat);
                }
            }
            else
            {
                CRat super1 = new CRat();
                super1.model = ratModel;
                super1.position = new Vector3(5000, 0, 0);
                super1.velocity = super1.position;
                super1.velocity.Normalize();
                super1.size = 50;
                super1.health = 300;
                super1.superRat = true;
                super1.leftFrontLeg = super1.model.Bones.IndexOf(super1.model.Bones["LeftFrontLeg"]);
                super1.rightFrontLeg = super1.model.Bones.IndexOf(super1.model.Bones["RightFrontLeg"]);
                super1.leftBackLeg = super1.model.Bones.IndexOf(super1.model.Bones["LeftBackLeg"]);
                super1.rightBackLeg = super1.model.Bones.IndexOf(super1.model.Bones["RightBackLeg"]);
                super1.tail1 = super1.model.Bones.IndexOf(super1.model.Bones["Tail1"]);
                rats.AddLast(super1);

                CRat super2 = new CRat();
                super2.model = ratModel;
                super2.position = new Vector3(-5000, 0, 0);
                super2.velocity = super2.position;
                super2.velocity.Normalize();
                super2.size = 50;
                super2.health = 300;
                super2.superRat = true;
                super2.leftFrontLeg = super2.model.Bones.IndexOf(super2.model.Bones["LeftFrontLeg"]);
                super2.rightFrontLeg = super2.model.Bones.IndexOf(super2.model.Bones["RightFrontLeg"]);
                super2.leftBackLeg = super2.model.Bones.IndexOf(super2.model.Bones["LeftBackLeg"]);
                super2.rightBackLeg = super2.model.Bones.IndexOf(super2.model.Bones["RightBackLeg"]);
                super2.tail1 = super2.model.Bones.IndexOf(super2.model.Bones["Tail1"]);
                rats.AddLast(super2);
            }
        }

        #region Update

        /// <summary>
        /// This function is called to update this component of our game
        /// to the current game time.
        /// </summary>
        /// <param name="gameTime"></param>
        public void Update(GameTime gameTime)
        {
            for (LinkedListNode<CRat> ratNode = rats.First; ratNode != null; )
            {
                LinkedListNode<CRat> nextAsteroid = ratNode.Next;
                CRat rat = ratNode.Value;

                float runSpeed = .05f;        // Seconds

                if (rat.legAngel < 0.30f && !rat.reachedEnd)
                {
                    rat.legAngel += (float)(0.20 * gameTime.ElapsedGameTime.TotalSeconds / runSpeed);
                    if (rat.legAngel > 0.20f)
                    {
                        rat.legAngel = 0.20f;
                        rat.reachedEnd = true;
                    }
                }
                else if (rat.legAngel > -0.5f && rat.reachedEnd)
                {
                    rat.legAngel -= (float)(0.20 * gameTime.ElapsedGameTime.TotalSeconds / runSpeed);
                    rat.reachedEnd = true;
                    if (rat.legAngel < -0.2f)
                    {
                        rat.legAngel = -0.2f;
                        rat.reachedEnd = false;
                    }
                }
                rat.position -= 1f * rat.velocity;

                ratNode = nextAsteroid;
            }

        }
        #endregion

        #region Draw

        /// <summary>
        /// This function is called to draw this game component.
        /// </summary>
        /// <param name="graphics"></param>
        /// <param name="gameTime"></param>
        public void Draw(GraphicsDeviceManager graphics, GameTime gameTime)
        {
            foreach (CRat rat in rats)
            {
                double x = rat.position.X;
                double z = rat.position.Z;
                double angle = Math.Atan2(-x, -z);
                Matrix scaleTrans = Matrix.CreateScale(rat.size) *
                    Matrix.CreateRotationY((float) angle) *
                    Matrix.CreateTranslation(rat.position);
                DrawModel(graphics, rat.model, scaleTrans, rat);
            }
        }

        #endregion

        private void DrawModel(GraphicsDeviceManager graphics, Model model, Matrix world, CRat rat)
        {
            Matrix[] transforms = new Matrix[model.Bones.Count];
            model.CopyAbsoluteBoneTransformsTo(transforms);

            int count = model.Bones.Count;
            for (int i = 0; i < count; i++)
            {
                if (i == rat.tail1)
                {
                    transforms[rat.tail1] = Matrix.CreateRotationX(rat.legAngel + .3f);
                }
                else
                    transforms[i] = Matrix.Identity;
                ModelBone bone = model.Bones[i];
                if (bone.Parent == null)
                {
                    transforms[i] *= bone.Transform;
                }
                else
                {
                    transforms[i] *= bone.Transform * transforms[bone.Parent.Index];
                }
            }
            transforms[rat.leftFrontLeg] = Matrix.CreateRotationX(rat.legAngel) * transforms[rat.leftFrontLeg];
            transforms[rat.rightFrontLeg] = Matrix.CreateRotationX(-rat.legAngel) * transforms[rat.rightFrontLeg];
            transforms[rat.leftBackLeg] = Matrix.CreateRotationX(rat.legAngel) * transforms[rat.leftBackLeg];
            transforms[rat.rightBackLeg] = Matrix.CreateRotationX(-rat.legAngel) * transforms[rat.rightBackLeg];

            foreach (ModelMesh lMesh in model.Meshes)
            {
                foreach (BasicEffect lEffect in lMesh.Effects)
                {
                    lEffect.EnableDefaultLighting();
                    lEffect.World = transforms[lMesh.ParentBone.Index] * world;
                    lEffect.View = game.Camera.View;
                    lEffect.Projection = game.Camera.Projection;
                }
                lMesh.Draw();
            }
        }

        public bool TestLaserForCollision(Vector3 position)
        {
            for (LinkedListNode<CRat> ratNode = rats.First; ratNode != null; )
            {
                LinkedListNode<CRat> nextNode = ratNode.Next;
                CRat rat = ratNode.Value;

                // Obtain a bounding sphere for the asteroid.  I can get away
                // with this here because I know the model has exactly one mesh
                // and exactly one bone.
                BoundingSphere bs = rat.model.Meshes[rat.model.Meshes.IndexOf(rat.model.Meshes["Body"])].BoundingSphere;
                bs = bs.Transform(rat.model.Bones[rat.model.Bones.IndexOf(rat.model.Bones["Body"])].Transform);

                // Move this to world coordinates.  Note how easy it is to 
                // transform a bounding sphere
                bs.Radius *= rat.size;
                bs.Center += rat.position;

                if ((position - bs.Center).LengthSquared() < bs.Radius * bs.Radius)
                {
                    
                    rat.health -= 1;
                    // We have a hit.  We'll delete this asteroid and return true
                    game.SoundBank.PlayCue("explosion2");
                    if (rat.health == 0)
                    {
                        
                        if (rat.superRat)
                        {
                            score += 1000;
                            for (int i = 0; i < 20; i++)
                            {
                                CRat rat2 = new CRat();
                                rat2.model = ratModel;
                                Vector3 addVec = RandomVector(-1000, 1000);
                                addVec.X = 0;
                                addVec.Y = 0; 
                                rat2.position = rat.position + addVec;
                                rat2.velocity = rat2.position;
                                rat2.velocity.Normalize();
                                int velFactor = random.Next(10);
                                if(velFactor != 0)
                                    rat2.velocity *= velFactor;
                                rat2.size = 10;

                                rat2.leftFrontLeg = rat2.model.Bones.IndexOf(rat2.model.Bones["LeftFrontLeg"]);
                                rat2.rightFrontLeg = rat2.model.Bones.IndexOf(rat2.model.Bones["RightFrontLeg"]);
                                rat2.leftBackLeg = rat2.model.Bones.IndexOf(rat2.model.Bones["LeftBackLeg"]);
                                rat2.rightBackLeg = rat2.model.Bones.IndexOf(rat2.model.Bones["RightBackLeg"]);

                                rat2.tail1 = rat2.model.Bones.IndexOf(rat2.model.Bones["Tail1"]);

                                rats.AddLast(rat2);
                            }
                        }
                        else
                            score += 100;
                        rats.Remove(ratNode);
                    }
                    return true;
                }

                ratNode = nextNode;
            }

            return false;
        }

        public bool TestForDeath(BoundingSphere aBSphere)
        {
            for (LinkedListNode<CRat> ratNode = rats.First; ratNode != null; )
            {
                LinkedListNode<CRat> nextNode = ratNode.Next;
                CRat rat = ratNode.Value;
                BoundingSphere lBS = rat.model.Meshes[rat.model.Meshes.IndexOf(rat.model.Meshes["Body"])].BoundingSphere;
                lBS.Radius *= rat.size;
                lBS.Center += rat.position;
                if (aBSphere.Intersects(lBS) )
                {
                    return true;
                }
                ratNode = nextNode;
            }

            return false;
        }

        public void Reset()
        {
            rats.Clear();
            score = 0;
            while (rats.Count < numSpawn)
            {
                if (!spawnSuperRats)
                {
                    while (rats.Count < numSpawn)
                    {
                        CRat rat = new CRat();
                        rat.model = ratModel;
                        rat.position = RandomVector(-3000, 3000);
                        rat.position.Y = 0;
                        rat.velocity = rat.position;
                        rat.velocity.Normalize();
                        int velFactor = random.Next(3);
                        if (velFactor != 0)
                            rat.velocity *= velFactor;
                        rat.size = 10;

                        if (rat.position.Length() < 2000)
                            continue;

                        rat.leftFrontLeg = rat.model.Bones.IndexOf(rat.model.Bones["LeftFrontLeg"]);
                        rat.rightFrontLeg = rat.model.Bones.IndexOf(rat.model.Bones["RightFrontLeg"]);
                        rat.leftBackLeg = rat.model.Bones.IndexOf(rat.model.Bones["LeftBackLeg"]);
                        rat.rightBackLeg = rat.model.Bones.IndexOf(rat.model.Bones["RightBackLeg"]);

                        rat.tail1 = rat.model.Bones.IndexOf(rat.model.Bones["Tail1"]);

                        rats.AddLast(rat);
                    }
                }
                else
                {
                    CRat super1 = new CRat();
                    super1.model = ratModel;
                    super1.position = new Vector3(5000, 0, 0);
                    super1.velocity = super1.position;
                    super1.velocity.Normalize();
                    super1.size = 50;
                    super1.health = 300;
                    super1.superRat = true;
                    super1.leftFrontLeg = super1.model.Bones.IndexOf(super1.model.Bones["LeftFrontLeg"]);
                    super1.rightFrontLeg = super1.model.Bones.IndexOf(super1.model.Bones["RightFrontLeg"]);
                    super1.leftBackLeg = super1.model.Bones.IndexOf(super1.model.Bones["LeftBackLeg"]);
                    super1.rightBackLeg = super1.model.Bones.IndexOf(super1.model.Bones["RightBackLeg"]);
                    super1.tail1 = super1.model.Bones.IndexOf(super1.model.Bones["Tail1"]);
                    rats.AddLast(super1);

                    CRat super2 = new CRat();
                    super2.model = ratModel;
                    super2.position = new Vector3(-5000, 0, 0);
                    super2.velocity = super2.position;
                    super2.velocity.Normalize();
                    super2.size = 50;
                    super2.health = 300;
                    super2.superRat = true;
                    super2.leftFrontLeg = super2.model.Bones.IndexOf(super2.model.Bones["LeftFrontLeg"]);
                    super2.rightFrontLeg = super2.model.Bones.IndexOf(super2.model.Bones["RightFrontLeg"]);
                    super2.leftBackLeg = super2.model.Bones.IndexOf(super2.model.Bones["LeftBackLeg"]);
                    super2.rightBackLeg = super2.model.Bones.IndexOf(super2.model.Bones["RightBackLeg"]);
                    super2.tail1 = super2.model.Bones.IndexOf(super2.model.Bones["Tail1"]);
                    rats.AddLast(super2);
                }
            }

        }

        #region RandomVector
        private Vector3 RandomVector(float min, float max)
        {
            return new Vector3((float)(min + (random.NextDouble() * (max - min))),
                (float)(min + (random.NextDouble() * (max - min))),
                (float)(min + (random.NextDouble() * (max - min))));
        }
        #endregion

    }
}